﻿//////////////////////////////////////////////////////////////////////
// <copyright file=MainWindow.cs">
//     Copyright (c) 2010
//     Released under  GNU GENERAL PUBLIC LICENSE
//     http://dev.perl.org/licenses/gpl1.html
// </copyright>
// <date> 3/29/2010 6:29:30 PM </date>
// <author> B. Endrovski </author>
// <web> http://furiouspixels.blogspot.com/ </web>
//////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Drawing2D;

namespace ContourReconstruction
{
	public partial class MainWindow : Form
	{
		private Vector2D[] points;
		private Reconstructor reconstructor;

		// Drawing paremeters
		private const float frame = 20;
		private float scale;
		private float xoffset;
		private float yoffset;

		public MainWindow()
		{
			InitializeComponent();

			controlStrip.Enabled = false;

			triangleMeshToolStripMenuItem.Checked = true;
			activeTrianglesToolStripMenuItem.Checked = true;
			activeCirclesToolStripMenuItem.Checked = true;
			contourToolStripMenuItem.Checked = true;
			activeEdgesToolStripMenuItem.Checked = true;
			vValueToolStripMenuItem.Checked = true;
		}

		private void CalculateScaling()
		{
			if(points == null)
				return;

			// Init 
			float xmin = Single.MaxValue;
			float ymin = Single.MaxValue;
			//
			float xmax = Single.MinValue;			
			float ymax = Single.MinValue;
			//
			for (int i = 0; i < points.Length; i++)
			{
				Vector2D p = points[i];
				//
				if (p.X < xmin) xmin = p.X;
				if (p.X > xmax) xmax = p.X;
				if (p.Y > ymax) ymax = p.Y;
				if (p.Y < ymin) ymin = p.Y;
			}

			// Make sure the scaling is best fit, uniform and 
			// leaves some framing pixels on the edges
			xoffset = xmin;
			yoffset = ymin;
			float w = TheContainer.ContentPanel.Width - 2 * frame;
			float h = TheContainer.ContentPanel.Height - 2 * frame;
			//
			float xscale = w / (xmax - xmin);
			float yscale = h / (ymax - ymin);
			scale = (xscale > yscale) ? yscale : xscale;
		}

		private void openToolStripMenuItem_Click(object sender, EventArgs e)
		{			
			if (openFileDialog.ShowDialog() == DialogResult.OK)
			{
				// File read
				String filename = openFileDialog.FileName;			
				try
				{
					TextReader r = new StreamReader(filename);
					string s = r.ReadLine();
					int size =  Int32.Parse(s);
					string[] sarray = new string[2];
					points = new Vector2D[size];
					//
					for (int i = 0; i < size; i++)
					{
						s = r.ReadLine();
						sarray = s.Split(' ');
						float x = Single.Parse(sarray[0]);
						float y = Single.Parse(sarray[1]);
						points[i] = new Vector2D(x, y);
					}

					reconstructor = new Reconstructor(points);
					controlStrip.Enabled = true;
				}
				catch (Exception ex)
				{
					MessageBox.Show("Error reading file", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			} // endif			

			CalculateScaling();
			RefreshScreen();
		} // event hndl


		private float ScaleX(float x) { return (x - xoffset) * scale + frame; }
		private float ScaleY(float y) { return (y - yoffset) * scale + frame; }

		private void DrawLine(Graphics g, Color c, float width, Vector2D from, Vector2D to)
		{			
			g.DrawLine(
				new Pen(new SolidBrush(c), width),
				ScaleX(from.X),
				g.ClipBounds.Height - ScaleY(from.Y),
				ScaleX(to.X),
				g.ClipBounds.Height - ScaleY(to.Y));
		}

		private void DrawCirlce(Graphics g, Color c, float width, Vector2D center, float radius)
		{
			g.DrawEllipse(
				new Pen(new SolidBrush(c), width),
				ScaleX(center.X) - radius * scale,
				g.ClipBounds.Height - ScaleY(center.Y) - radius * scale,
				2 * radius * scale,
				2 * radius * scale);
		}

		private void FillRect(Graphics g, Color c, Vector2D center, float size)
		{
			g.FillRectangle(
				new SolidBrush(c),
				ScaleX(center.X) - size / 2.0f,
				g.ClipBounds.Height -  ScaleY(center.Y) - size / 2.0f,
				size, size);
		}

		private void DrawText(Graphics g, Color c, String text, Vector2D center, float size)
		{
			g.DrawString(
				text,
				new Font("Arial", size, FontStyle.Regular),
				new SolidBrush(c),
				ScaleX(center.X) - size / 2.0f,
				g.ClipBounds.Height - ScaleY(center.Y) - size / 2.0f);
		}

		private void RefreshScreen()
		{
			// Uses an image to buffer the draw and avoid screen flicker
			// (Using the Paint method gave too much flicker
			Image b = (Image)new Bitmap(pictureBox.Width, pictureBox.Height);
			Graphics g = Graphics.FromImage(b);
			g.SetClip(new RectangleF(0, 0, pictureBox.Width, pictureBox.Height));
			g.CompositingMode = CompositingMode.SourceOver;
			g.SmoothingMode = SmoothingMode.HighSpeed;

			// Draw points
			if (points != null && points.Length >= 1)
				foreach (Vector2D pf in points)
					FillRect(g, Color.Purple, pf, 4);

			// Draw triangles
			if (reconstructor != null)
			{
				if (reconstructor.ContourVertices != null)
					foreach (Vector2D pf in reconstructor.ContourVertices)
						FillRect(g, Color.Cyan, pf, 6);

				// Triangle mesh
				if (triangleMeshToolStripMenuItem.Checked)
					foreach (Edge edge in reconstructor.Edges)
						DrawLine(g, Color.Black, 1, edge.A, edge.B);

				// Active triangles (being added or removed
				if (activeTrianglesToolStripMenuItem.Checked)
					foreach (Triangle t in reconstructor.ActiveTriangles)
						for (int i = 0; i < 3; i++)
							DrawLine(g, Color.Blue, 1, t.Vertices[i], t.Vertices[(i + 1) % 3]);

				// Their circumscribed circles
				if (activeCirclesToolStripMenuItem.Checked)
					foreach (Triangle t in reconstructor.ActiveTriangles)
						DrawCirlce(g, Color.Blue, 1, t.Circumcenter, t.Circumradius);

				// Contour
				if (contourToolStripMenuItem.Checked)
					foreach (Edge edge in reconstructor.ContourEdges)
						DrawLine(g, Color.Green, 2, edge.A, edge.B);

				// Active edges (polygon)
				if (activeEdgesToolStripMenuItem.Checked)
					foreach (Edge edge in reconstructor.ActiveEdges)
						DrawLine(g, Color.Red, 3, edge.A, edge.B);

				if (vValueToolStripMenuItem.Checked && reconstructor.IsDelaunay)
					foreach (Triangle t in reconstructor.ActiveTriangles)
						DrawText(g, Color.Blue, t.M.ToString(), t.Center, 9.0f);
					
			} // end if

			pictureBox.Image = b;
		} // event hndl

		private void MainWindow_ResizeEnd(object sender, EventArgs e)
		{
			CalculateScaling();
			RefreshScreen();
		} // event hndl

		private void nextStepBtn_Click(object sender, EventArgs e)
		{
			if (reconstructor != null)
			{
				reconstructor.NextStep();			
				RefreshScreen();
			}
		} // event hndl

		private void playerTimer_Tick(object sender, EventArgs e)
		{
			if (reconstructor != null)
			{
				long time = DateTime.Now.Ticks;
				reconstructor.NextStep();
				time = DateTime.Now.Ticks - time;
				time /= 100;
				Console.WriteLine("Done in: " + time + " milliseconds.");
				RefreshScreen();
			}
		} // event hndl

		private void autoPlayBtn_Click(object sender, EventArgs e)
		{
			playerTimer.Start();
		} // event hndl

		private void pauseBtn_Click(object sender, EventArgs e)
		{
			playerTimer.Stop();
		} // event hndl

		private void ToolStripMenuItem_Click(object sender, EventArgs e)
		{
			RefreshScreen();
		} // event hndl

		private void jumpFwdBtn_Click(object sender, EventArgs e)
		{
			if (reconstructor != null)
			{
				if (!reconstructor.IsDelaunay)
				{
					while (!reconstructor.IsDelaunay)
						reconstructor.NextStep();
					reconstructor.NextStep();
				}
				else if (!reconstructor.IsDone)
				{
					while(!reconstructor.IsDone)
						reconstructor.NextStep();
				}
			}
			RefreshScreen();
		} // event hndl

		private void helpToolStripMenuItem_Click(object sender, EventArgs e)
		{
			// A single help file for both the release and the debug version of the exe
			DirectoryInfo di = new DirectoryInfo(Path.GetDirectoryName(Application.ExecutablePath));
			di = di.Parent.Parent.Parent;
			//
			try
			{
				System.Diagnostics.Process.Start(di.FullName + "\\Contour Reconstruction.pdf");
			}
			catch (Exception)
			{
				MessageBox.Show("No help file found at " + di.FullName + "\\Contour Reconstruction.pdf", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		} // event hndl

	} // class
}
